package com.gitee.wsl.func.chain

/**
 * 源码来自：https://github.com/MarioAriasC/funKTionale/blob/master/funktionale-utils/src/main/kotlin/org/funktionale/utils/partialfunctions.kt
 * 虽然他称之为偏函数，实际不是，看起来更像是一个约定，可以用于实现责任链，他的实现思想参考了偏函数
 */
class PartialFunction<in P1, out R>(
    /*用于判断入参[p1]是否会被偏函数的函数体执行*/
    private val definedAt: (P1) -> Boolean,
    /*满足条件时，真实执行的函数体[f]*/
    private val f: (P1) -> R
) : (P1) -> R {
    /*偏函数实际是一个类，通过从写 [invoke] 函数，伪装成方法*/
    override fun invoke(p1: P1): R {
        if (definedAt(p1)) {
            return f(p1)
        } else {
            throw IllegalArgumentException("Value: ($p1) isn't supported by this function")
        }
    }
    /*偏函数本身对外暴露，判断函数*/
    fun isDefinedAt(p1: P1) = definedAt(p1)
}

/**
 * 给当前的偏函数一个入参[p1]，当 [p1] 满足函数 [PartialFunction.isDefinedAt]，执行偏函数的函数体
 * 不满足时使用 [default] 作为返回值
 */
fun <P1, R> PartialFunction<P1, R>.invokeOrElse(p1: P1, default: R): R {
    return if (this.isDefinedAt(p1)) {
        this(p1)
    } else {
        default
    }
}

/**
 * 用于连接多个偏函数，形成函数链条
 */
operator fun <P1, R> PartialFunction<P1, R>.plus(that: PartialFunction<P1, R>): PartialFunction<P1, R> {
    return PartialFunction({ this.isDefinedAt(it) || that.isDefinedAt(it) }) {
        when {
            this.isDefinedAt(it) -> this(it)
            that.isDefinedAt(it) -> that(it)
            else -> throw IllegalArgumentException("function not definet for parameter ($it)")
        }
    }
}

/**
 * 将一个普通函数转换成偏函数(类)
 */
fun <P1, R> ((P1) -> R).toPartialFunction(definedAt: (P1) -> Boolean): PartialFunction<P1, R> {
    return PartialFunction(definedAt, this)
}

// https://github.com/MarioAriasC/funKTionale/blob/master/funktionale-utils/src/main/kotlin/org/funktionale/utils/partialfunctions.kt

infix fun <P1, R> PartialFunction<P1, R>.orElse(that: PartialFunction<P1, R>): PartialFunction<P1, R> {
    return PartialFunction({ this.isDefinedAt(it) || that.isDefinedAt(it) }) {
        when {
            this.isDefinedAt(it) -> this(it)
            that.isDefinedAt(it) -> that(it)
            else -> throw IllegalArgumentException("function not definet for parameter ($it)")
        }
    }
}


/**
 * /**
 *  * 使用自运行Lambda来构建一个个 PartialFunction 实例：部长、会长、学院
 *  *
 *  * @author GitLqr
 *  */
 * val groupLeader = {
 *     val definetAt: (ApplyEvent) -> Boolean = { it.money <= 200 }
 *     val handler: (ApplyEvent) -> Unit = { println("Group Leader handled application: ${it.title}.") }
 *     PartialFunction(definetAt, handler)
 * }()
 * val president = {
 *     val definetAt: (ApplyEvent) -> Boolean = { it.money <= 500 }
 *     val handler: (ApplyEvent) -> Unit = { println("President handled application: ${it.title}.") }
 *     PartialFunction(definetAt, handler)
 * }()
 * val college = {
 *     val definetAt: (ApplyEvent) -> Boolean = { true }
 *     val handler: (ApplyEvent) -> Unit = {
 *         when {
 *             it.money <= 1000 -> println("College handled application: ${it.title}.")
 *             else -> println("College: This application is refused.")
 *         }
 *     }
 *     PartialFunction(definetAt, handler)
 * }()
 *
 * // 使用
 * // val applyChain = groupLeader.orElse(president.orElse(college))
 * val applyChain = groupLeader orElse president orElse college // 中缀表达式
 * applyChain(ApplyEvent(10, "buy a pen")) // 买只钢笔
 * applyChain(ApplyEvent(200, "team building")) // 团建
 * applyChain(ApplyEvent(600, "hold a debate match")) // 举行辩论赛
 * applyChain(ApplyEvent(1200, "annual meeting of the college")) // 学院年会
 *
 * // 输出
 * Group Leader handled application: buy a pen.
 * Group Leader handled application: team building.
 * College handled application: hold a debate match.
 * College: This application is refused.
 *
 */